import { MobilePage } from './mobilePage';
export const componentsDir = 'components';
export const pageClassName = 'mobile-page';
import * as React from 'react';
import { ADMIN_APP } from '../share/common';
import lessc = require('lessc');
import * as chitu from "maishu-chitu";

export interface IMobilePageDesigner {
    selecteControl(control: Control<any, any>, controlType: React.ComponentClass<any>);
}

//==============================================================    
export interface ComponentProp<T> extends React.Props<T> {
    onClick?: (event: MouseEvent, control: T) => void,
    createElement?: (type, props, ...children) => JSX.Element,
}
export interface ControlProps<T> extends React.Props<T> {
    mobilePage: MobilePage,
    controlDirectory: string,
}
export interface ControlConstructor {
    new(props): Control<any, any>
}
export abstract class Control<P extends ControlProps<any>, S> extends React.Component<P, S> {
    private _page: MobilePage;
    private _elementPage: chitu.Page;
    private _state: S;

    protected hasCSS = false;

    stateChanged = chitu.Callbacks<this, any>();
    id: string;

    constructor(props) {
        super(props);
        this._page = this.props.mobilePage;
        this._elementPage = this.props.mobilePage.props.elementPage;
    }

    abstract get persistentMembers(): (keyof S)[];
    abstract _render(h): JSX.Element | JSX.Element[];

    get mobilePage() {
        return this._page;
    }
    get elementPage() {
        return this._elementPage;
    }

    get app() {
        return this.elementPage.app as chitu.Application;
    }

    get state(): S {
        return this._state;
    }

    /**
     * 重写 set state， 在第一次赋值，将 props 的持久化成员赋值过来。 
     */
    set state(value: S) {
        value = value || {} as S;
        if (this._state != null) {
            this._state = value;
            return;
        }

        var state = {} as any;
        let keys = this.persistentMembers || [];
        for (let i = 0; i < keys.length; i++) {
            var prop = (this.props as any)[keys[i]];
            if (prop !== undefined)
                state[keys[i]] = prop;
        }

        this._state = Object.assign(value, state);;
    }

    get hasEditor() {
        return true;
    }


    // private setStateTimes = 0;
    setState(f: (prevState: S, props: P) => S, callback?: () => any): void;
    setState(state: Partial<S>, callback?: () => any): void;
    setState(state: any, callback?: () => any) {
        //=====================================================
        // 忽略第一次设置，把第一次设置作为初始化
        // if (this.setStateTimes > 0)
        this.stateChanged.fire(this, state);
        //=====================================================

        // this.setStateTimes = this.setStateTimes + 1;
        return super.setState(state, callback);
    }

    componentWillReceiveProps(props: P) {
        // debugger;
        let keys = this.persistentMembers || [];
        for (let i = 0; i < keys.length; i++) {
            var prop = (props as any)[keys[i]];
            if (prop !== undefined)
                this.state[keys[i]] = prop;
        }
    }

    render() {

        if (this.hasCSS)
            this.loadControlCSS();

        if (this.mobilePage.props.designTime != null)
            return this._render(createDesignTimeElement);

        return this._render(React.createElement);
    }

    static async loadLessFile(color: StyleColor, control: React.Component<any, any>, path: string) {
        var typeName = control.constructor.name;
        typeName = typeName.replace('Control', '');
        typeName = typeName[0].toLowerCase() + typeName.substr(1);

        let style = document.head.querySelector(`style[name="${typeName}"]`) as HTMLStyleElement;
        if (!style) {
            style = document.createElement('style');
            style.type = 'text/css';
            style.setAttribute('name', typeName);
            document.head.appendChild(style);
        }

        if (color == style.getAttribute('color')) {
            return;
        }

        style.setAttribute('color', color);

        if (!path)
            path = `${componentsDir}/${typeName}/control`;

        let lessText = `@import "${path}";`;
        if (color != 'default') {
            let pathname = location.pathname;
            if (pathname.endsWith("/")) {
                pathname = pathname.substring(0, pathname.length - 1);
            }
            lessText = lessText + `@import "${pathname}/components/style/colors/${color}.less";`;
        }

        let parser = new lessc.Parser(window['less']);
        parser.parse(lessText, (err, tree) => {
            if (err) {
                console.error(err);
                return;
            }

            style.innerHTML = tree.toCSS();
        })
    }

    protected async loadControlCSS() {
        let color = this.mobilePage.styleColor || 'default';
        let cssPath = `${this.props.controlDirectory}/control`
        return Control.loadLessFile(color, this, cssPath);
    }

    subscribe<T>(item: chitu.ValueStore<T>, callback: (value: T) => void) {
        let func = item.add(callback);
        let componentWillUnmount = (this as any).componentWillUnmount as () => void;
        (this as any).componentWillUnmount = function () {
            item.remove(func);
            componentWillUnmount();
        }
    }

    get isRuntime(): boolean {
        return window[ADMIN_APP] == null;
    }

    static get isDesignTime(): boolean {
        return window[ADMIN_APP] != null;
    }

    static elementOnClick(e: HTMLElement, callback: (event: MouseEvent) => void | Promise<any>,
        args?: {
            confirm?: string | (() => string);
            toast?: string | HTMLElement;
        }) {

        args = args || {};
        let isDesignTime = this.isDesignTime;

        if (isDesignTime) {
            return;
        }

        ui.buttonOnClick(e as HTMLButtonElement, (event) => {
            var result = callback(event);
            if (typeof result == 'object')
                return result;

            return Promise.resolve();
        }, args)
    }
}

export function createDesignTimeElement(type: string | React.ComponentClass<any>, props: ComponentProp<any>, ...children) {
    props = props || {};
    if (typeof type == 'string')
        props.onClick = () => { };
    else if (typeof type != 'string' && (type as any != React.Fragment)) {
        props.onClick = (event, control: Control<any, any>) => {
            if (control.context != null) {
                control.context.designer.selecteControl(control, type);
            }
        }
    }
    if (type == 'a' && (props as any).href) {
        (props as any).href = 'javascript:';
    }

    let args = [type, props];
    for (let i = 2; i < arguments.length; i++) {
        args[i] = arguments[i];
    }
    return React.createElement.apply(React, args);
}

export let components: { [key: string]: React.ComponentClass<any> } = {};
export function component(name: string) {
    return function (constructor: React.ComponentClass<any>) {
        components[name] = constructor;
    }
}

export interface ComponentClass<T> extends React.ComponentClass<T> {
    attributes: { editorPath?: string, editorExport?: string }
}

export interface ControlState {
    persistentMembers: (keyof this)[];
}

